--[[
	This file is part of FlexBar3.

	FlexBar3 is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	FlexBar3 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with FlexBar3.  If not, see <http://www.gnu.org/licenses/>.
]]
-- Local upvalues
local CreateFrame = CreateFrame;
local strsub = strsub;
local tostring = tostring;
local Button = FlexBar3:GetModule("Button");
local ButtonClass = Button:NewButtonClass("Button", 1);
local SecureStateDriverFrame = Button.SecureStateDriverFrame;
-- Due to the way the OO interface handles name conflicts this function will be called when doing :Enable(), :Disable() or :Load() and the other ones will be ignored, because of that we have to call them ourselves
-- Load handler

local function StartDragging(Dragger)
	ButtonClass.StartDragging(Dragger.Object);
end

local function StopDragging(Dragger)
	ButtonClass.StopDragging(Dragger.Object);
end

function ButtonClass:Load()
	-- Create the actual frame, inheriting from SecureActionButtonTemplate. Set a name for it, as sadly, some blizzard functions require a frame name instead of a reference
	local FrameName = "FB2_BTN_" .. strsub(tostring(self), 8);
	self.FrameName = FrameName
	local Frame = CreateFrame("Button", FrameName, UIParent, "SecureActionButtonTemplate");
	self.Frame = Frame;
	Frame.Object = self;

	-- Strata and sizing, drag registering
	Frame:SetFrameStrata("MEDIUM");
	Frame:SetFrameLevel(1);
	Frame:SetWidth(36);
	Frame:SetHeight(36);

	-- Textures setu
	Frame:SetPushedTexture("Interface\\Buttons\\UI-Quickslot-Depress");
	Frame:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square");
	Frame:GetHighlightTexture():SetBlendMode("ADD");

	-- Icon setup
	Frame.Icon = self.Frame:CreateTexture(nil, "BACKGROUND")
	Frame.Icon:SetAllPoints(Frame);

	-- Cooldown ticker setup
	Frame.Cooldown = CreateFrame("Cooldown", nil, self.Frame, "CooldownFrameTemplate");

	-- NormalTexture setup
	self.Frame:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
	local NormalTexture = self.Frame:GetNormalTexture();
	NormalTexture:SetWidth(60);
	NormalTexture:SetHeight(60);
	NormalTexture:ClearAllPoints();
	NormalTexture:SetPoint("CENTER", 1, -1);

	-- Button settings
	Frame:RegisterForClicks("AnyUp");
	Frame:EnableMouse(true);
	Frame:SetClampedToScreen(true);
	Frame:SetMovable(true);
	Frame:RegisterForDrag("LeftButton");

	-- Dragger frame
	Frame.Dragger = CreateFrame("Frame");
	local Dragger = Frame.Dragger;
	Dragger.Object = self;
	Dragger:SetFrameStrata("MEDIUM");
	Dragger:SetFrameLevel(3);
	Dragger:SetAllPoints(Frame);
	Dragger.Texture = Dragger:CreateTexture(nil,'Overlay');
	Dragger.Texture:SetTexture(0, 0.6, 0, 0.5);

	Dragger.Texture:SetAllPoints(Dragger);
	Dragger:EnableMouse(true);
	Dragger:SetClampedToScreen(true);
	Dragger:SetMovable(false);
end

function ButtonClass:SetDefaults()
	local StartHeight = UIParent:GetHeight() - UIParent:GetHeight() / 3;
	local StartWidth = UIParent:GetWidth() / 5;
	local ButtonNumber = tonumber(self.Name);
	if(not self.StateDB.DefaultState) then 
		self.StateDB.DefaultState = {};
	end
	if(ButtonNumber) then
		self.StateDB.DefaultState.X = StartWidth + (37 * (ButtonNumber - ((math.ceil(ButtonNumber / 12) - 1) * 12) - 1));
		self.StateDB.DefaultState.Y = StartHeight - (37 * (math.ceil(ButtonNumber / 12) - 1));
	else
		self.StateDB.DefaultState.X = StartWidth;
		self.StateDB.DefaultState.Y = StartHeight;
	end
	self.StateDB.DefaultState.Show = true;
	self.StateDB.DefaultState.Lock = false;
	self.StateDB.DefaultState.Scale = 1;
	self.StateDB.DefaultState.Texture = nil;
end

function ButtonClass:LoadSettings()
	-- We can't do ApplyAll() in OnEnable because it requires the
	-- AttributeClass to be fully loaded, which happens shortly after this
	-- class' LoadSettings call, too bad ... call it in Enable instead
end

function ButtonClass:Enable(ButtonName)
	self:ApplyAll();
	self:RegisterScript("OnShow", self.ApplyLocking);
	self:RegisterCallback("StatesUpdated", self.ApplyAll);
end

function ButtonClass:Disable()
	self:Hide();
	self:SetAttribute("showstates", "!*");
	self:UnregisterScript("OnShow", self.ApplyLocking);
end

function ButtonClass:SetType(Type)
	local OldType = self.DB.Type;
	self.DB.Type = Type;
	self:TriggerCallback("TypeChanged", OldType, Type);
end

function ButtonClass:ApplyAll()
	self:ApplyPositioning();
	self:ApplyVisibility();
	self:ApplyScaling();
	self:ApplyLocking();
	self:ApplyOverrideTexturing();
end

function ButtonClass:Lock()
	self.Frame.Dragger:Hide();
	self.Frame.Dragger:SetScript("OnMouseDown", nil);
	self.Frame.Dragger:SetScript("OnMouseUp", nil);
	self:TriggerCallback("OnLock");
end

local function StartDragging(this)
	local self = this.Object;
	self:StartDragging();
end

local function StopDragging(this)
	local self = this.Object;
	self:StopDragging();
end

function ButtonClass:Unlock()
	self.Frame.Dragger:Show();
	self.Frame.Dragger:SetScript("OnMouseDown", StartDragging);
	self.Frame.Dragger:SetScript("OnMouseUp", StopDragging);
	self:TriggerCallback("OnUnlock");
end

function ButtonClass:ApplyLocking()
	if(self.StateDB[self:GetUnmodifiedState()].Lock) then
		self:Lock();
	else
		self:Unlock();
	end
end

function ButtonClass:Show()
	self.Frame:Show();
	
end

function ButtonClass:Hide()
	self.Frame:Hide();
	self:Lock();
end

function ButtonClass:ApplyVisibility()
	local StateList = self.ActiveStateList;
	local ShowStates = "";
	for i = 1, #StateList do
		local CurrentState = StateList[i];
		if(self.StateDB["*%s" .. CurrentState].Show == true) then
			ShowStates = ("%s,%s"):format(ShowStates, CurrentState);
		end
	end
	if(self.StateDB[self:GetUnmodifiedState()].Show) then
		self:Show();
	else
		self:Hide();
	end
end

function ButtonClass:GetScale()
	return self.Frame:GetScale();
end

function ButtonClass:SetScale(Scale)
	self.Frame:SetScale(Scale);
end

function ButtonClass:ApplyScaling()
	local StateList = self.ActiveStateList;
	for i = 1, #StateList do
		local CurrentState = StateList[i];
		self:SetStateAttribute(CurrentState, "scale", tostring(self.StateDB["*%s" .. CurrentState].Scale));
	end
	self:SetScale(self.StateDB[self:GetUnmodifiedState()].Scale or 1);
end

function ButtonClass:GetPosition()
	local Frame = self.Frame;
	local Scale = self:GetScale();
	return Frame:GetLeft() * Scale, Frame:GetBottom() * Scale;
end

function ButtonClass:SetPosition(X, Y)
	self.Frame:SetPoint("BOTTOMLEFT", SecureStateDriverFrame, "BOTTOMLEFT", X, Y);
end

function ButtonClass:ApplyPositioning()
	local StateList = self.ActiveStateList;
	for i = 1, #StateList do
		local CurrentState = StateList[i];
		self:SetStateAttribute(CurrentState, "ofsx", tostring(self.StateDB["*%s" .. CurrentState].X));
		self:SetStateAttribute(CurrentState, "ofsy", tostring(self.StateDB["*%s" .. CurrentState].Y));
		self:SetStateAttribute(CurrentState, "ofspoint", "BOTTOMLEFT");
	end
	local CurrentState = self:GetUnmodifiedState();
	self:SetPosition(self.StateDB[CurrentState].X, self.StateDB[CurrentState].Y);
end

function ButtonClass:GetTexture()
	return self.Frame.Icon:GetTexture();
end

function ButtonClass:SetTexture(Texture)
	return self.Frame.Icon:SetTexture(self.StateDB[self:GetModifiedState()].Texture or Texture);
end

function ButtonClass:GetOverrideTexture()
	return self.StateDB[self:GetModifiedState()].Texture;
end

function ButtonClass:SetOverrideTexture(Texture)
	self.StateDB[self:GetModifiedState()].Texture = Texture;
end

function ButtonClass:ApplyOverrideTexturing()
	local Texture = self.StateDB[self:GetModifiedState()].Texture;
	if(Texture) then
		self:SetTexture(Texture);
	end
end

function ButtonClass:StartDragging()
	self:TriggerCallback("OnDragStart");
	self.Frame:StartMoving();
end

function ButtonClass:StopDragging()
	self.Frame:StopMovingOrSizing();
	local X, Y = self:GetPosition();
	local CurrentState = self:GetUnmodifiedState();
	-- Check if there are any non-default settings for positioning for this state, if there are, only modify settings for that state, if not, modify the default settings
	local CurrentStateDB = rawget(self.StateDB, CurrentState);
	if(CurrentStateDB and (CurrentStateDB.X or CurrentStateDB.Y)) then
		self.StateDB[CurrentState].X = X;
		self.StateDB[CurrentState].Y = Y;
	else
		self.StateDB.DefaultState.X = X;
		self.StateDB.DefaultState.Y = Y;
	end
	
	self:ApplyPositioning();
	self:TriggerCallback("OnDragStop");
end
